package tjs.study.notes.dotest.proxy.javassist.agent;

import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import sun.management.VMManagement;
import tjs.study.notes.dotest.TjsStudyNotesApplication;
import tjs.study.notes.dotest.proxy.service.UserService;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class DoTestAgent {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        int jvmPID = jvmPid();
        System.out.println("jvmPID: " + jvmPID);

        // 类首次加载前，载入Agent，则可以新增方法等
        //loadAgent(jvmPID);

        UserService userService = new UserService("test");
        userService.login();
        userService.logout();//static final修饰的方法也能被代理
        Class<?> classBef = Class.forName(UserService.class.getName());

        // 类首次加载后，载入Agent，则不可以新增方法等(DoLoadAgent.main也会报这个错)
        //loadAgent(jvmPID);//class redefinition failed: attempted to add a method

        // 不研究idea插件，这里直接通过java反射调用
        //userService.invokeGetUserName();

        // 即使没有重新new一个对象，依然会生效
        while (true){
            userService.login();
            Class<?> classAft = Class.forName(UserService.class.getName());
            System.out.println(classAft==classBef);
            userService.sleep();
        }
    }

    public static void loadAgent(int jvmPID) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException {
        VirtualMachine virtualMachine = VirtualMachine.attach(String.valueOf(jvmPID));
        virtualMachine.loadAgent(TjsStudyNotesApplication.work_space + "/proxy/javassist/agent/AgentmainTraTest.jar");
    }

    public static final int jvmPid() {
        try {
            RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
            Field jvm = runtime.getClass().getDeclaredField("jvm");
            jvm.setAccessible(true);
            VMManagement mgmt = (VMManagement) jvm.get(runtime);
            Method pidMethod = mgmt.getClass().getDeclaredMethod("getProcessId");
            pidMethod.setAccessible(true);
            int pid = (Integer) pidMethod.invoke(mgmt);
            return pid;
        } catch (Exception e) {
            return -1;
        }
    }
}